package com.ruoyi.business.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.bizsys.dto.PayTrans;
import com.ruoyi.business.common.config.WalletConfig;
import com.ruoyi.business.domain.*;
import com.ruoyi.business.service.IPayTransService;
import com.ruoyi.business.service.IWalletService;
import com.ruoyi.common.constant.WalletConstants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.RspEnmu;
import com.ruoyi.common.exception.BusinessException;
import com.ruoyi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;

/**
 * @Author: Jamnsin
 * @Date: 2022/6/10 20:11
 */
@Slf4j
@Service
public class WalletServiceImpl extends BaseService implements IWalletService {

    @Autowired
    WalletConfig walletConfig;

    @Resource
    RestTemplate restTemplate;

    @Autowired
    RedisCache redisCache;

    @Lazy
    @Autowired
    IPayTransService payTransService;

    @Override
    public void walletWithdraw(WalletWithdrawReq walletWithdrawReq) {
        log.info("进入提现流程 >>> walletWithdrawReq = {}", walletWithdrawReq);

        String custNo = getCustNo2();
        walletWithdrawReq.setCustNo(custNo);
        WalletOrderReq walletOrderReq = buildWalletOrderReq(walletWithdrawReq.getWalletType(), custNo);

        try {
            long start = System.currentTimeMillis();

            JSONObject walletWithdrawRst = invokeWalletCreateOrder(walletWithdrawReq);
            String orderSerialNo = walletWithdrawRst.getString("orderSerialNo");
            BigDecimal actualAmount = walletWithdrawRst.getBigDecimal("actualAmount");
            walletOrderReq.setOrderSerialNo(orderSerialNo);
            log.debug("订单流水号：{}", orderSerialNo);

            checkActualAmount(walletWithdrawReq.getWalletType(), actualAmount);

            if (StringUtils.equals(WalletConstants.WALLET_TYPE_BALANCE_1, walletWithdrawReq.getWalletType())) {
                log.info("钱包余额提现需等待账务审核，不直接进入提现");
                log.info("结束提现流程 >>> ");
                return;
            }

            PayTrans payTrans = buildPaystackTransfer(walletWithdrawReq, orderSerialNo, actualAmount);
            payTransService.payTrans(payTrans);

            // 修改钱包订单状态（Pending）
            walletOrderReq.setApprovalStatus(WalletConstants.WALLET_ORDER_STATUS_PENDING);
            this.updateWalletOrder(walletOrderReq);

            log.info("结束提现流程 >>> 耗时：{} ms", (System.currentTimeMillis() - start));
        } catch (BusinessException e) {
            log.error("钱包提现异常 >>> custNo：{}，code：{}, error：{}，detail：{}", custNo, e.getCode(), e.getMessage(), e.getDetails());
            errorProcess(walletOrderReq, e);
            throw e;
        } catch (Exception e) {
            log.error("钱包提现 Error >>> custNo：{}，error：", custNo, e);
            // 修改钱包订单状态（失败）
            orderFailuer(walletOrderReq, WalletConstants.WALLET_ORDER_STATUS_FAILED, e.getMessage());
            throw new BusinessException(RspEnmu.WALLET_WITHDRAW_ERROR);
        }
    }

    private void checkActualAmount(String walletType, BigDecimal actualAmount) {
        if (actualAmount.compareTo(BigDecimal.ZERO) <= 0) {
            if (StringUtils.equals(WalletConstants.WALLET_TYPE_BALANCE_1, walletType)) {
                throw new BusinessException(RspEnmu.WALLET_WITHDRAW_INSUFFICIENT_BALANCE);
            }
            if (StringUtils.equals(WalletConstants.WALLET_TYPE_BONUS_2, walletType)) {
                throw new BusinessException(RspEnmu.WALLET_WITHDRAW_INSUFFICIENT_BONUS);
            }
        }
    }

    @Override
    public JSONObject walletRepayment(WalletRepayReq walletRepayReq) {
        log.info("调用钱包服务创建还款订单 >>> orderType = {}，walletRepayReq = {}", walletRepayReq.getOrderType(), walletRepayReq);

        JSONObject orderReq = (JSONObject) JSON.toJSON(walletRepayReq);
        return restTemplate.postForObject(createOrderApi(walletRepayReq.getCustNo(), walletRepayReq.getOrderType()), orderReq, JSONObject.class).getJSONObject("data");
    }

    @Override
    public void updateWalletOrder(WalletOrderReq walletOrderReq) {
        log.info("调用钱包服务更新钱包订单状态 >>> orderStatus = {}, walletOrderReq = {}", walletOrderReq.getApprovalStatus(), walletOrderReq);
        if (StringUtils.isBlank(walletOrderReq.getOrderSerialNo())) {
            log.info("walletOrderReq.OrderSerialNo 为空，不更新");
            return;
        }
        restTemplate.postForObject(walletConfig.getUpdateOrder(), walletOrderReq, JSONObject.class).getJSONObject("data");
    }

    @Override
    public void blockWallet(String custNo, String walletLock) {
        log.info("调用钱包服务更新钱包激活状态 >>> custNo = {}, walletLock = {}", custNo, walletLock);
        WalletStatusReq walletStatusReq = WalletStatusReq.builder().custNo(custNo).walletStatus(walletLock).build();
        restTemplate.postForObject(walletConfig.getWalletStatus(), walletStatusReq, JSONObject.class).getJSONObject("data");
    }

    @Override
    public JSONObject getCustomersWithBalance() {
        log.info("调用钱包服务获取存在余额的客户 >>>");
        return restTemplate.getForObject(walletConfig.getCustomersWithBalance(), JSONObject.class).getJSONObject("data");
    }

    @Override
    public BigDecimal getSumAmountByFoundsType(String custNo, String fundsType) {
        String url = sumAmountByFoundsType(custNo, fundsType);
        return restTemplate.getForObject(url, JSONObject.class).getBigDecimal("data");
    }

    @Override
    public void orderApproval(WalletOrderVo walletOrder, String approvalStatus) {
        log.info("订单审核结果处理流程 >>> orderSerialNo = {}, approvalStatus = {}, walletOrder = {}", walletOrder.getSerialNo(), approvalStatus, walletOrder);

        String custNo = walletOrder.getCustNo();

        WalletOrderReq walletOrderReq = buildWalletOrder(walletOrder);
        if (StringUtils.equals(WalletConstants.WALLET_ORDER_STATUS_REJECTED, approvalStatus)) {
            // 修改钱包订单状态（拒绝）
            orderFailuer(walletOrderReq, WalletConstants.WALLET_ORDER_STATUS_REJECTED, walletOrderReq.getMessage());
            return;
        }

        // 修改钱包订单状态（审核通过）
        walletOrderReq.setApprovalStatus(WalletConstants.WALLET_ORDER_STATUS_APPROVED);
        this.updateWalletOrder(walletOrderReq);

        try {
            WalletWithdrawReq walletWithdrawReq = getWalletWithdrawReq(walletOrder);
            PayTrans payTrans = buildPaystackTransfer(walletWithdrawReq, walletOrder.getSerialNo(), walletOrder.getAmount());
            payTransService.payTrans(payTrans);

            // 修改钱包订单状态（Pending）
            walletOrderReq.setApprovalStatus(WalletConstants.WALLET_ORDER_STATUS_PENDING);
            this.updateWalletOrder(walletOrderReq);

            log.info("结束提现流程 >>> ");
        } catch (BusinessException e) {
            log.error("钱包提现异常 >>> custNo：{}，code：{}, error：{}，detail：{}", custNo, e.getCode(), e.getMessage(), e.getDetails());
            errorProcess(walletOrderReq, e);
            throw e;
        } catch (Exception e) {
            log.error("钱包提现 Error >>> custNo：{}，error：", custNo, e);
            orderFailuer(walletOrderReq, WalletConstants.WALLET_ORDER_STATUS_FAILED, e.getMessage());
            throw new BusinessException(RspEnmu.WALLET_WITHDRAW_ERROR);
        }
    }

    private void orderFailuer(WalletOrderReq walletOrderReq, String orderStatus, String message) {
        walletOrderReq.setMessage(message);
        walletOrderReq.setApprovalStatus(orderStatus);
        this.updateWalletOrder(walletOrderReq);
    }

    private void errorProcess(WalletOrderReq walletOrderReq, BusinessException e) {
        //放款号已存在,不改变订单状态
        if (RspEnmu.REFERENCE_ALREADY_EXISTS_ON_TRANSFER.getCode().equals(e.getCode())) {
            log.warn("Reference is already exists on third platform，reference = {}", walletOrderReq.getOrderSerialNo());
        }
        //如果不是超时和异常,那么均是失败
        if (!RspEnmu.NETWORK_BUSY.getCode().equals(e.getCode())) {
            orderFailuer(walletOrderReq, WalletConstants.WALLET_ORDER_STATUS_FAILED, e.getDetails());
        }
    }

    private WalletWithdrawReq getWalletWithdrawReq(WalletOrderVo walletOrder) {
        WalletWithdrawReq walletWithdrawReq = new WalletWithdrawReq();
        walletWithdrawReq.setCustNo(walletOrder.getCustNo());
        walletWithdrawReq.setWalletType(walletOrder.getWalletType());
        walletWithdrawReq.setApplyAmount(walletOrder.getAmount());
        walletWithdrawReq.setBankCode(walletOrder.getBankCode());
        walletWithdrawReq.setBankAccount(walletOrder.getBankAccount());
        return walletWithdrawReq;
    }

    private WalletOrderReq buildWalletOrder(WalletOrderVo walletOrder) {
        return WalletOrderReq.builder().walletType(walletOrder.getWalletType()).orderType(WalletConstants.ORDER_TYPE_WITHDRAW).orderSerialNo(walletOrder.getSerialNo()).reviewer(walletOrder.getReviewer()).custNo(walletOrder.getCustNo()).build();
    }

    @Override
    public void updateWalletAccount(WalletAccountReq walletAccountReq) {
        log.info("调用钱包服务更新钱包余额，新增收入明细 >>> walletAccountReq = {}", walletAccountReq);
        restTemplate.postForObject(walletAccountApi(walletAccountReq.getCustNo()), walletAccountReq, JSONObject.class).getJSONObject("data");
    }

    @Override
    public void initWalletAccount(String custNo) {
        log.info("调用钱包服务初始化钱包账户 >>> custNo = {}", custNo);
        restTemplate.postForObject(walletAccountInitApi(custNo), null, JSONObject.class).getJSONObject("data");
    }

    private JSONObject invokeWalletCreateOrder(WalletWithdrawReq walletWithdrawReq) {
        String orderType = getWithdrawOrderType(walletWithdrawReq.getWalletType());
        log.info("调用钱包服务创建提现订单 >>> orderType = {}，walletWithdrawReq = {}", orderType, walletWithdrawReq);

        JSONObject orderReq = JSON.parseObject(JSON.toJSONString(walletWithdrawReq));
        JSONObject orderRes = restTemplate.postForObject(createOrderApi(walletWithdrawReq.getCustNo(), WalletConstants.ORDER_TYPE_REFUND), orderReq, JSONObject.class).getJSONObject("data");
        return orderRes;
    }

    private String getWithdrawOrderType(String walletType) {
        if (StringUtils.equals(WalletConstants.WALLET_ORDER_TYPE_BALANCE, walletType)) {
            return WalletConstants.ORDER_TYPE_REFUND;
        }

        if (StringUtils.equals(WalletConstants.WALLET_ORDER_TYPE_BONUS, walletType)) {
            return WalletConstants.ORDER_TYPE_WITHDRAW;
        }

        throw new BusinessException(RspEnmu.WALLET_WITHDRAW_TYPE_CANNOT_SUPPORT);
    }

    private PayTrans buildPaystackTransfer(WalletWithdrawReq walletWithdrawReq, String orderSerialNo, BigDecimal amount) {
        return PayTrans.builder()
                .custNo(walletWithdrawReq.getCustNo())
                .accountNumber(walletWithdrawReq.getBankAccount())
                .bankCode(walletWithdrawReq.getBankCode())
                .loanAmt(amount)
                .payTransSerialNo(orderSerialNo)
                .userId(getUserIdByCustNo(walletWithdrawReq.getCustNo()))
                .build();
    }

    private WalletOrderReq buildWalletOrderReq(String walletType, String custNo) {
        return WalletOrderReq.builder().walletType(walletType).orderType(WalletConstants.ORDER_TYPE_WITHDRAW).custNo(custNo).build();
    }

    @Override
    public JSONObject getWalletAccount(String custNo) {
        return restTemplate.getForObject(walletAccountApi(custNo), JSONObject.class).getJSONObject("data");
    }

    @Override
    public JSONArray getWalletIncome() {
        return restTemplate.getForObject(walletFlowApi(this.getCustNo(), WalletConstants.WALLET_FLOW_INCOME), JSONObject.class).getJSONArray("data");
    }

    @Override
    public JSONArray getWalletExpend() {
        return restTemplate.getForObject(walletFlowApi(this.getCustNo(), WalletConstants.WALLET_FLOW_EXPEND), JSONObject.class).getJSONArray("data");
    }

    private String createOrderApi(String custNo, String orderType) {
        return walletConfig.getCreateOrder().replace("CUST_NO", custNo).replace("ORDER_TYPE", orderType);
    }

    private String walletFlowApi(String custNo, String flowType) {
        return walletConfig.getWalletFlow().replace("CUST_NO", custNo).replace("FLOW_TYPE", flowType);
    }

    private String walletAccountApi(String custNo) {
        return walletConfig.getWalletAccount().replace("CUST_NO", custNo);
    }

    private String walletAccountInitApi(String custNo) {
        return walletConfig.getWalletAccountInit().replace("CUST_NO", custNo);
    }


    private String sumAmountByFoundsType(String custNo, String flowType) {
        return walletConfig.getSumAmountByFoundsType().replace("CUST_NO", custNo).replace("FLOW_TYPE", flowType);
    }

}
